﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Extensions.Options;
using Secs4Net;
using Secs4Net.Sml;

namespace PXFAP.SecsNetWinApplication
{
    public partial class Form1 : Form
    {
        SecsGem? _secsGem;
        HsmsConnection? _connector;
        readonly ISecsGemLogger _logger;
        private readonly object o = new object();
        /// <summary>
        /// 设备列表
        /// </summary>
        List<Equipment> equipments = null;
        public Form1()
        {
            InitializeComponent();
            _logger = new SecsLogger(this);
            equipments = new List<Equipment>();
        }

        private async void btnEnable_Click(object sender, EventArgs e)
        {
            #region 测试DGV控件
            //Task.Run(() =>
            //{
            //    while (true)
            //    {
            //        equipments.Add(Equipment.Configure(option =>
            //        {
            //            option.EquipmentId = 123;
            //            option.Host = "127.0.0.1";
            //            option.Port = 12;
            //            option.State = "selected";
            //        }));

            //        this.Invoke(new Action(() =>
            //        {
            //            int index = this.dgvEquipments.Rows.Add();
            //            this.dgvEquipments.Rows[index].Cells[0].Value = "123";
            //        }));

            //        Thread.Sleep(1000);
            //    }
            //});
            #endregion

            #region Checking 
            if (equipments.Any(s => s.Port == (int)numPort.Value))
            {
                MessageBox.Show($"端口号:{numPort.Value}已有设备占用", "警告：",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                return;
            }

            if (equipments.Any(s => s.EquipmentId == (ushort)numDeviceId.Value))
            {
                MessageBox.Show($"设备号:{numDeviceId.Value}已存在连接", "警告：",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                return;
            }
            #endregion

            _secsGem?.Dispose();

            if (_connector is not null)
            {
                await _connector.DisposeAsync();
            }

            var options = Options.Create(new SecsGemOptions
            {
                IsActive = radioActiveMode.Checked,
                IpAddress = txtAddress.Text,
                Port = (int)numPort.Value,
                SocketReceiveBufferSize = (int)numBufferSize.Value,
                DeviceId = (ushort)numDeviceId.Value,
            });

            await Connect(options);
        }

        /// <summary>
        /// 连接 机台
        /// </summary>
        /// <param name="options">连接机台相关参数</param>
        /// <returns></returns>
        private async Task Connect(IOptions<SecsGemOptions> options)
        {
            _secsGem?.Dispose();

            if (_connector is not null)
            {
                await _connector.DisposeAsync();
            }
             
            var secsGem = SecsGemWarp.Configure(option =>
            {
                option.Connector = new HsmsConnection(options, _logger);
                option.SecsGem = new SecsGem(options, option.Connector, _logger);
                option.CancellationToken = new CancellationTokenSource();
            });
            secsGem.OnStarted += SecsGemOnStarted;
            secsGem.OnReceivePrimaryMessaged += ReceivePrimaryMessaged;
            secsGem.StartAsync(options.Value);
        }

        /// <summary>
        /// 监控 socket 连接状态
        /// </summary>
        /// <param name="secsWarp"></param>
        /// <param name="secsGemOptions"></param>
        private void SecsGemOnStarted(
            SecsGemWarp secsWarp,
            SecsGemOptions secsGemOptions)
        {
            secsWarp.Connector.ConnectionChanged += (s, e) =>
            {
                lock (o)
                {
                    var lastIndex = equipments.FindLastIndex(f => f.EquipmentId.Equals(secsWarp.SecsGem.DeviceId));
                    if (lastIndex < 0)
                    {
                        equipments.Add(Equipment.Configure(option =>
                        {
                            option.EquipmentId = secsGemOptions.DeviceId;
                            option.Host = secsGemOptions.IpAddress;
                            option.Port = secsGemOptions.Port;
                            option.State = e.ToString();
                            option.Mode = secsGemOptions.IsActive ? "主动" : "被动";
                            option.SocketReceiveBufferSize = secsGemOptions.SocketReceiveBufferSize;
                            option.SecsGem = secsWarp.SecsGem;
                            option.OnDispose = secsWarp.Dispose;
                        }));
                    }
                    else
                    {
                        // 防止重新上线  释放对象不一致
                        equipments[lastIndex].OnDispose = secsWarp.Dispose;
                        equipments[lastIndex].SecsGem = secsWarp.SecsGem;

                        equipments[lastIndex].State = e.ToString();
                    }
                    EquipmentBindings(lastIndex);
                }
            };
        }

        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="primaryMessage"></param>
        private void ReceivePrimaryMessaged(PrimaryMessageWrapper receivedMessage)
        {
            this.Invoke(new Action(() =>
            {
                this.rtbReceivePrimaryMessage.Text +=
                  receivedMessage?.PrimaryMessage.ToSml();
            }));
        }

        public void EquipmentBindings(int lastIndex)
        {
            this.Invoke(new Action(() =>
            {
                if (lastIndex < 0)
                {
                    int index = this.dgvEquipments.Rows.Add();
                    var row = equipments.Last();

                    Action<int, object> action = (i, s) =>
                    {
                        this.dgvEquipments.Rows[index].Cells[i].Value = s;
                    };
                    action(0, row.EquipmentId);
                    action(1, row.Host);
                    action(2, row.Port);
                    action(3, row.Mode);
                    action(4, row.SocketReceiveBufferSize);
                    action(5, row.State);
                }
                else
                {
                    this.dgvEquipments.Rows[lastIndex].Cells[5].Value =
                        equipments[lastIndex].State;
                }
            }));
        }

        internal class SecsLogger : ISecsGemLogger
        {
            readonly Form1 _form;
            internal SecsLogger(Form1 form)
            {
                _form = form;
            }
            public void MessageIn(SecsMessage msg, int id)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Black;
                    _form.richTextBox1.AppendText($"<-- [0x{id:X8}] {msg.ToSml()}\n");
                });
            }

            public void MessageOut(SecsMessage msg, int id)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Black;
                    _form.richTextBox1.AppendText($"--> [0x{id:X8}] {msg.ToSml()}\n");
                });
            }

            public void Info(string msg)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Blue;
                    _form.richTextBox1.AppendText($"{msg}\n");
                });
            }

            public void Warning(string msg)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Green;
                    _form.richTextBox1.AppendText($"{msg}\n");
                });
            }

            public void Error(string msg, SecsMessage? message, Exception? ex)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Red;
                    _form.richTextBox1.AppendText($"{msg}\n");
                    _form.richTextBox1.AppendText($"{message?.ToSml()}\n");
                    _form.richTextBox1.SelectionColor = Color.Gray;
                    _form.richTextBox1.AppendText($"{ex}\n");
                });
            }

            public void Debug(string msg)
            {
                _form.Invoke((MethodInvoker)delegate
                {
                    _form.richTextBox1.SelectionColor = Color.Yellow;
                    _form.richTextBox1.AppendText($"{msg}\n");
                });
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OfflineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var rowIndex = this.dgvEquipments?.CurrentCell?.RowIndex ?? -1;
            if (rowIndex < 0)
                return;

            this.Invoke(new Action(() =>
            {
                this.equipments[rowIndex].OnDispose(() =>
                {
                    this.dgvEquipments.Rows[rowIndex].Cells[5].Value = "Connecting";
                });
            }));
        }

        /// <summary>
        /// 从 Connecting 状态连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void OnlineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var selectIndex = this.dgvEquipments?.CurrentCell?.RowIndex ?? -1;
            if (selectIndex < 0)
                return;

            if (this.dgvEquipments.Rows[selectIndex].Cells[5].Value.ToString() != "Connecting")
                return;

            //重新上线操作
            await Connect(equipments[selectIndex].GetSecsGemOptions());
        }

        /// <summary>
        /// Send Primary Message
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnSendPrimaryMessage_Click(object sender, EventArgs e)
        {
            var primaryMessage = this.rtbPrimaryMessage.Text.Trim();
            if (string.IsNullOrEmpty(primaryMessage))
            {
                MessageBox.Show("send primary message is empty", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var selectIndex = this.dgvEquipments?.CurrentCell?.RowIndex ?? -1;
            if (selectIndex < 0)
            {
                MessageBox.Show("请选择要发送的机台", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var equipment = equipments[selectIndex];
            if (equipment.SecsGem is null || equipment.State != "Selected")
            {
                return;
            }

            try
            {
                var reply = await equipment.SecsGem.SendAsync(primaryMessage.ToSecsMessage());
                rtbReceivePrimaryMessage.Text = reply.ToSml();
            }
            catch (SecsException ex)
            {
               rtbReceivePrimaryMessage.Text = ex.Message;
            }
            catch (Exception ex)
            {
                rtbReceivePrimaryMessage.Text = ex.Message;
            }
        }
    }
}
